#pragma once

#define REGISTER_PROPERTY(propertyName) \
        { \
            static AVUI::PropertyInfo propertyInfo(_thisType::get_##propertyName##Property()); \
            s_type.RegisterProperty(&propertyInfo); \
        } \

namespace AVUI
{
    class SimpleProperty;

void* GetPtrFromTypeHelper(void* p);

template<class T>
T* GetPtrFromType(void* p)
{
    if((get_Type( (T*) 0).get_IsValueType()))
    {
        p = GetPtrFromTypeHelper(p);
    }

    return reinterpret_cast<T*>(p);
}

class EnumClass;

template<class U>
ObjectPtr BoxValueIfRequired(Property* pProperty, const char* sz, U* pDummy)
{
    return (Object*)BoxValue(sz);
}

template<class U>
ObjectPtr BoxValueIfRequired(Property* pProperty, const WCHAR* wz, U* pDummy)
{
    return (Object*)BoxValue(wz);
}

template<class T, class U>
ObjectPtr BoxValueIfRequired(Property* pProperty, T* pT, U* pDummy)
{
    return (Object*)pT;
}

template<class T, class U>
ObjectPtr BoxValueIfRequired(Property* pProperty, TRefCountedPtr<T> pT, U* pDummy)
{
    return (Object*)(pT.get_Ptr());
}

template<class T, class U>
ObjectPtr BoxValueIfRequired(Property* pProperty, const T* pT, const U* pDummy)
{
    return (Object*)pT;
}

template<class U>
ObjectPtr BoxValueIfRequired(Property* pProperty, const char* sz, const U* pDummy)
{
    return (Object*)BoxValue(sz);
}

template<class U>
ObjectPtr BoxValueIfRequired(Property* pProperty, const WCHAR* wz, const U* pDummy)
{
    return (Object*)BoxValue(wz);
}

template<class T>
ObjectPtr BoxValueIfRequired(Property* pProperty, const T& t, EnumClass*)
{
    return BoxValue<int>(static_cast<int>(t));
}

template<class T>
ObjectPtr BoxValueIfRequired(Property* pProperty, const T& t, ...)
{
    return BoxValue(t);
}

template<class W, class V>
V* UnboxValueIfRequired(Property* pProperty, Object* pValue, void (W::*pfnSetter)(V* v), V* pDummy)
{
    return (V*) pValue;
}

template<class W, class V>
const V* UnboxValueIfRequired(Property* pProperty, Object* pValue, void (W::*pfnSetter)(const V* v), V* pDummy)
{
    return (const V*) pValue;
}

template<class W, class V>
V UnboxValueIfRequired(Property* pProperty, Object* pValue, void (W::*pfnSetter)(V v), EnumClass*)
{
    return static_cast<V>(UnboxValue<int>(pValue));
}

template<class W, class V>
V UnboxValueIfRequired(Property* pProperty, Object* pValue, void (W::*pfnSetter)(const V& v), ...)
{
    return UnboxValue<V>(pValue);
}

template<class W, class V>
V UnboxValueIfRequired(Property* pProperty, Object* pValue, void (W::*pfnSetter)(V v), ...)
{
    return UnboxValue<V>(pValue);
}


};


/////////////////////////////////////////////////////
// New property model

#define DECLARE_DEPENDENCY_PROPERTY_BEGIN(propertyName) \
    static AVUI::TRefCountedPtr<AVUI::DependencyProperty> propertyName##Property; \
    static AVUI::DependencyProperty* get_##propertyName##Property() { \
        AVUI::DependencyProperty* pCurrentProperty = propertyName##Property; \

#define DECLARE_DEPENDENCY_PROPERTY_END() \
    return pCurrentProperty; } \

#define DECLARE_DEPENDENCY_PROPERTY(propertyName) \
    DECLARE_DEPENDENCY_PROPERTY_BEGIN(propertyName) \
    DECLARE_DEPENDENCY_PROPERTY_END() \

#define DECLARE_READONLY_DEPENDENCY_PROPERTY_BEGIN(propertyName) \
    static AVUI::TRefCountedPtr<AVUI::DependencyPropertyKey> propertyName##PropertyKey; \
    static AVUI::DependencyPropertyKey* get_##propertyName##PropertyKey() { return propertyName##PropertyKey; } \
    DECLARE_DEPENDENCY_PROPERTY_BEGIN(propertyName) \

#define DECLARE_READONLY_DEPENDENCY_PROPERTY_END() \
    DECLARE_DEPENDENCY_PROPERTY_END() \

#define DECLARE_READONLY_DEPENDENCY_PROPERTY(propertyName) \
    DECLARE_READONLY_DEPENDENCY_PROPERTY_BEGIN(propertyName) \
    DECLARE_READONLY_DEPENDENCY_PROPERTY_END() \


#define DECLARE_PROPERTY_BEGIN(propertyName, propertyType, isContent) \
    struct propertyName##FunctionBinding \
    { \
        static AVUI::ObjectPtr GetValueThunk(AVUI::Property* pProperty, AVUI::Object* pObject) { return AVUI::BoxValueIfRequired(pProperty, AVUI::GetPtrFromType<_thisType>(pObject)->get_##propertyName(), (propertyType*) NULL); } \
        static void SetValueThunk(AVUI::Property* pProperty, AVUI::Object* pObject, AVUI::Object* pValue) { return AVUI::GetPtrFromType<_thisType>(pObject)->set_##propertyName(AVUI::UnboxValueIfRequired(pProperty, pValue, &(_thisType::set_##propertyName), (propertyType*) NULL)); } \
    }; \
    static AVUI::Property* get_##propertyName##Property() { \
        static AVUI::SimpleProperty property(#propertyName, GET_TYPE(propertyType), isContent ? SimplePropertyFlags::IsContentProperty : SimplePropertyFlags::None, &propertyName##FunctionBinding::GetValueThunk, &propertyName##FunctionBinding::SetValueThunk); \
        AVUI::SimpleProperty* pCurrentProperty = &property; \

#define DECLARE_PROPERTY_END() \
    return pCurrentProperty; } \

#define DECLARE_PROPERTY(propertyName, propertyType, isContent) \
    DECLARE_PROPERTY_BEGIN(propertyName, propertyType, isContent) \
    DECLARE_PROPERTY_END() \


#define DECLARE_READONLY_PROPERTY_BEGIN(propertyName, propertyType, isContent) \
    struct propertyName##FunctionBinding \
    { \
        static AVUI::ObjectPtr GetValueThunk(AVUI::Property* pProperty, AVUI::Object* pObject) { return AVUI::BoxValueIfRequired(pProperty, AVUI::GetPtrFromType<_thisType>(pObject)->get_##propertyName(), (propertyType*) NULL); } \
    }; \
    static AVUI::Property* get_##propertyName##Property() { \
        static AVUI::SimpleProperty property(#propertyName, GET_TYPE(propertyType), isContent ? SimplePropertyFlags::IsContentProperty : SimplePropertyFlags::None, &propertyName##FunctionBinding::GetValueThunk, NULL); \

#define DECLARE_READONLY_PROPERTY_END() \
    return &property; } \

#define DECLARE_READONLY_PROPERTY(propertyName, propertyType, isContent) \
    DECLARE_READONLY_PROPERTY_BEGIN(propertyName, propertyType, isContent) \
    DECLARE_READONLY_PROPERTY_END() \


#define DECLARE_ATTRIBUTE_CONTENTPROPERTY() \
    static bool isContentPropertyRegistered = false; \
    if(!isContentPropertyRegistered) \
    { \
        pCurrentProperty->set_IsContentProperty(true); \
        isContentPropertyRegistered = true; \
    } \

#define DECLARE_ATTRIBUTE_AUTOCREATE() \
    static bool isAutoCreatePropertyRegistered = false; \
    if(!isAutoCreatePropertyRegistered) \
    { \
        pCurrentProperty->set_AutoCreateProperty(true); \
        isAutoCreatePropertyRegistered = true; \
    } \

#define DECLARE_ATTRIBUTE_CONVERTER(converterType) \
    static bool isPropertyConverterRegistered = false; \
    if(!isPropertyConverterRegistered) \
    { \
        static converterType converter; \
        pCurrentProperty->RegisterPropertyConverter(&converter); \
        isPropertyConverterRegistered = true; \
    } \

#define DECLARE_ATTRIBUTE_DEPENDSON(dependentProperty) \


/////////////////////////////////////////////////////







////////////////////////////////////////////////////////////////////////////
// Generic DP definition

#define IMPLEMENT_DEPENDENCY_PROPERTY_DEFINITION(propertyName, propertyType, defaultValue, propertyMetadata) \
    static AVUI::TRefCountedPtr<AVUI::DependencyProperty> propertyName##Property; \
    static AVUI::DependencyProperty* get_##propertyName##Property() { return propertyName##Property; } \

#define IMPLEMENT_REFERENCE_DEPENDENCY_PROPERTY_DEFINITION(propertyName, propertyType, defaultValue, propertyMetadata) \
    IMPLEMENT_DEPENDENCY_PROPERTY_DEFINITION(propertyName, propertyType, defaultValue, propertyMetadata)


// Reference object DP Definition
#define IMPLEMENT_REFERENCE_DEPENDENCY_PROPERTY(propertyName, propertyType, defaultValue, propertyMetadata) \
    IMPLEMENT_DEPENDENCY_PROPERTY_DEFINITION(propertyName, propertyType, defaultValue, propertyMetadata); \
    propertyType* get_##propertyName() { return AVUI::object_cast<propertyType>(GetValue(get_##propertyName##Property())); } \
    void set_##propertyName(propertyType* pValue) { SetValue(get_##propertyName##Property(), (Object*) pValue); } \

// Readonly reference object DP
#define IMPLEMENT_READONLY_REFERENCE_DEPENDENCY_PROPERTY(propertyName, propertyType, defaultValue, propertyMetadata) \
    IMPLEMENT_DEPENDENCY_PROPERTY_DEFINITION(propertyName, propertyType, defaultValue, propertyMetadata); \
    propertyType* get_##propertyName() { return AVUI::object_cast<propertyType>(GetValue(get_##propertyName##Property())); } \

//////////////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////////////////
// Value DP definition
#define IMPLEMENT_VALUE_DEPENDENCY_PROPERTY_DEFINITION(propertyName, propertyType, defaultValue, propertyMetadata) \
    IMPLEMENT_DEPENDENCY_PROPERTY_DEFINITION(propertyName, propertyType, AVUI::BoxValue(defaultValue), propertyMetadata)

#define IMPLEMENT_VALUE_DEPENDENCY_PROPERTY(propertyName, propertyType, defaultValue, propertyMetadata) \
    IMPLEMENT_VALUE_DEPENDENCY_PROPERTY_DEFINITION(propertyName, propertyType, defaultValue, propertyMetadata); \
    const propertyType &get_##propertyName() { return AVUI::UnboxValue< propertyType >(GetValue(get_##propertyName##Property())); } \
    void set_##propertyName(const propertyType& value) { SetValue(get_##propertyName##Property(), AVUI::BoxValue(value)); } \

#define IMPLEMENT_READONLY_VALUE_DEPENDENCY_PROPERTY(propertyName, propertyType, defaultValue, propertyMetadata) \
    IMPLEMENT_VALUE_DEPENDENCY_PROPERTY_DEFINITION(propertyName, propertyType, defaultValue, propertyMetadata); \
    const propertyType &get_##propertyName() { return AVUI::UnboxValue< propertyType >(GetValue(get_##propertyName##Property())); } \
///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
// Enum DP definition
#define IMPLEMENT_ENUM_DEPENDENCY_PROPERTY_DEFINITION(propertyName, propertyType, defaultValue, propertyMetadata) \
    IMPLEMENT_DEPENDENCY_PROPERTY_DEFINITION(propertyName, propertyType, AVUI::BoxValue((int)defaultValue), propertyMetadata)

#define IMPLEMENT_ENUM_DEPENDENCY_PROPERTY(propertyName, propertyType, defaultValue, propertyMetadata) \
    IMPLEMENT_ENUM_DEPENDENCY_PROPERTY_DEFINITION(propertyName, propertyType, defaultValue, propertyMetadata); \
    const propertyType::Enum get_##propertyName() { return (propertyType::Enum) UnboxValue< int >(GetValue(get_##propertyName##Property())); } \
    void set_##propertyName(const propertyType::Enum& value) { SetValue(get_##propertyName##Property(), AVUI::BoxValue((int)value)); } \

#define IMPLEMENT_READONLY_ENUM_DEPENDENCY_PROPERTY(propertyName, propertyType, defaultValue, propertyMetadata) \
    IMPLEMENT_ENUM_DEPENDENCY_PROPERTY_DEFINITION(propertyName, propertyType, defaultValue, propertyMetadata); \
    const propertyType::Enum get_##propertyName() { return (propertyType::Enum) UnboxValue< int >(GetValue(get_##propertyName##Property())); } \
///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
// Reference property definition
#define IMPLEMENT_REFERENCE_PROPERTY(propertyName, propertyType, isContent) \
    DECLARE_PROPERTY(propertyName, propertyType, isContent) \

#define IMPLEMENT_READONLY_REFERENCE_PROPERTY(propertyName, propertyType, isContent) \
    DECLARE_READONLY_PROPERTY(propertyName, propertyType, isContent) \
/////////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////////
// Value property definition
#define IMPLEMENT_VALUE_PROPERTY(propertyName, propertyType, isContent) \
    DECLARE_PROPERTY(propertyName, propertyType, isContent) \

#define IMPLEMENT_READONLY_VALUE_PROPERTY(propertyName, propertyType, isContent) \
    DECLARE_READONLY_PROPERTY(propertyName, propertyType, isContent) \


/////////////////////////////////////////////////////////////////////////////////
// Enum property definition
#define IMPLEMENT_ENUM_PROPERTY(propertyName, propertyType, isContent) \
    DECLARE_PROPERTY(propertyName, propertyType, isContent) \

#define IMPLEMENT_READONLY_ENUM_PROPERTY(propertyName, propertyType, isContent) \
    DECLARE_READONLY_PROPERTY(propertyName, propertyType, isContent) \

